﻿#include "screenwidget.h"  // 包含自定义的ScreenWidget头文件
#include "qmutex.h"        // 包含Qt的互斥锁头文件
#include "qapplication.h"  // 包含Qt应用程序头文件
#include "qpainter.h"      // 包含Qt绘图头文件
#include "qfiledialog.h"   // 包含Qt文件对话框头文件
#include "qevent.h"        // 包含Qt事件头文件
#include "qdatetime.h"     // 包含Qt日期时间头文件
#include "qstringlist.h"   // 包含Qt字符串列表头文件
#include "qdebug.h"        // 包含Qt调试头文件
#include "QMouseEvent"

// 根据Qt版本选择不同的屏幕获取方式
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
#include "qscreen.h"
#define deskGeometry qApp->primaryScreen()->geometry()  // 获取主屏幕的几何尺寸
#define deskGeometry2 qApp->primaryScreen()->availableGeometry()  // 获取主屏幕可用的几何尺寸
#else
#include "qdesktopwidget.h"
#define deskGeometry qApp->desktop()->geometry()  // 旧版本Qt获取屏幕几何尺寸
#define deskGeometry2 qApp->desktop()->availableGeometry()  // 旧版本Qt获取屏幕可用几何尺寸
#endif

// 定义一个宏，用于获取当前日期时间并转换为特定格式的字符串
#define STRDATETIME qPrintable (QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))

// Screen类的构造函数，初始化屏幕的最大宽度和高度，以及起始位置、结束位置等属性
Screen::Screen(QSize size)
{
    maxWidth = size.width();
    maxHeight = size.height();

    startPos = QPoint(-1, -1);
    endPos = startPos;
    leftUpPos = startPos;
    rightDownPos = startPos;
    status = SELECT;  // 设置初始状态为选择状态
}

// 获取屏幕的最大宽度
int Screen::width()
{
    return maxWidth;
}

// 获取屏幕的最大高度
int Screen::height()
{
    return maxHeight;
}

// 获取当前屏幕的状态
Screen::STATUS Screen::getStatus()
{
    return status;
}

// 设置屏幕的状态
void Screen::setStatus(STATUS status)
{
    this->status = status;
}

// 设置结束位置，并根据结束位置计算左上角和右下角的位置
void Screen::setEnd(QPoint pos)
{
    endPos = pos;
    leftUpPos = startPos;
    rightDownPos = endPos;
    cmpPoint(leftUpPos, rightDownPos);  // 调整左上角和右下角的位置，确保它们的顺序正确
}

// 设置起始位置
void Screen::setStart(QPoint pos)
{
    startPos = pos;
}

// 获取结束位置
QPoint Screen::getEnd()
{
    return endPos;
}

// 获取起始位置
QPoint Screen::getStart()
{
    return startPos;
}

// 获取左上角位置
QPoint Screen::getLeftUp()
{
    return leftUpPos;
}

// 获取右下角位置
QPoint Screen::getRightDown()
{
    return rightDownPos;
}

// 判断给定的位置是否在截图区域内
bool Screen::isInArea(QPoint pos)
{
    if (pos.x() > leftUpPos.x() && pos.x() < rightDownPos.x() && pos.y() > leftUpPos.y() && pos.y() < rightDownPos.y()) {
        return true;
    }

    return false;
}

// 移动截图区域
void Screen::move(QPoint p)
{
    int lx = leftUpPos.x() + p.x();
    int ly = leftUpPos.y() + p.y();
    int rx = rightDownPos.x() + p.x();
    int ry = rightDownPos.y() + p.y();

    // 确保截图区域不超出屏幕边界
    if (lx < 0) {
        lx = 0;
        rx -= p.x();
    }

    if (ly < 0) {
        ly = 0;
        ry -= p.y();
    }

    if (rx > maxWidth)  {
        rx = maxWidth;
        lx -= p.x();
    }

    if (ry > maxHeight) {
        ry = maxHeight;
        ly -= p.y();
    }

    leftUpPos = QPoint(lx, ly);
    rightDownPos = QPoint(rx, ry);
    startPos = leftUpPos;
    endPos = rightDownPos;
}

// 比较并调整左上角和右下角的点以确保顺序正确
void Screen::cmpPoint(QPoint &leftTop, QPoint &rightDown)
{
    QPoint l = leftTop;
    QPoint r = rightDown;

    if (l.x() <= r.x()) {
        if (l.y() <= r.y()) {
        } else {
            leftTop.setY(r.y());
            rightDown.setY(l.y());
        }
    } else {
        if (l.y() < r.y()) {
            leftTop.setX(r.x());
            rightDown.setX(l.x());
        } else {
            QPoint tmp;
            tmp = leftTop;
            leftTop = rightDown;
            rightDown = tmp;
        }
    }
}

// 使用单例模式获取ScreenWidget的实例
QPointer<ScreenWidget> ScreenWidget::self;
ScreenWidget *ScreenWidget::Instance()
{
    if (self.isNull()) {
        static QMutex mutex;  // 创建一个静态互斥锁
        QMutexLocker locker(&mutex);  // 锁定互斥锁
        if (self.isNull()) {
            self = new ScreenWidget;  // 创建并重置ScreenWidget实例
        }
    }

    return self.data();  // 返回ScreenWidget实例的数据指针
}

// ScreenWidget的构造函数，初始化菜单、屏幕对象和全屏图像
ScreenWidget::ScreenWidget(QWidget *parent) : QWidget(parent)
{
    // 设置窗口标志，使其成为一个工具窗口、始终保持在最前面、无边框等
    //this->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
    this->setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_DeleteOnClose); // 关闭时自动销毁

    // 确保窗口可以接收键盘事件
    this->setFocusPolicy(Qt::StrongFocus);
    this->grabKeyboard(); // 捕获键盘事件

    menu = new QMenu(this);  // 创建一个菜单对象
    menu->addAction("保存当前截图", this, SLOT(saveScreen()));  // 添加菜单项：保存当前截图
    menu->addAction("保存全屏截图", this, SLOT(saveFullScreen()));  // 添加菜单项：保存全屏截图
    menu->addAction("截图另存为", this, SLOT(saveScreenOther()));  // 添加菜单项：截图另存为
    menu->addAction("全屏另存为", this, SLOT(saveFullOther()));  // 添加菜单项：全屏另存为
    menu->addAction("退出截图", this, SLOT(hide()));  // 添加菜单项：退出截图

    // 获取屏幕大小并初始化Screen对象
    screen = new Screen(deskGeometry.size());
    // 创建一个QPixmap对象用于存储全屏图像
    fullScreen = new QPixmap();
}

ScreenWidget::~ScreenWidget()
{
    delete fullScreen;
    delete bgScreen;
    delete screen;
}

// 绘制事件处理函数
void ScreenWidget::paintEvent(QPaintEvent *)
{
    int x = screen->getLeftUp().x();
    int y = screen->getLeftUp().y();
    int w = screen->getRightDown().x() - x;
    int h = screen->getRightDown().y() - y;

    QPainter painter(this);  // 创建一个QPainter对象

    QPen pen;
    pen.setColor(Qt::green);  // 设置画笔颜色为绿色
    pen.setWidth(2);  // 设置画笔宽度
    pen.setStyle(Qt::DotLine);  // 设置画笔样式为虚线
    painter.setPen(pen);
    painter.drawPixmap(0, 0, *bgScreen);  // 绘制背景图像

    if (w != 0 && h != 0) {
        painter.drawPixmap(x, y, fullScreen->copy(x, y, w, h));  // 绘制截图区域的图像
    }

    painter.drawRect(x, y, w, h);  // 绘制截图矩形框

    pen.setColor(Qt::yellow);  // 设置画笔颜色为黄色
    painter.setPen(pen);
    painter.drawText(x + 2, y - 8, tr("截图范围：( %1 x %2 ) - ( %3 x %4 )  图片大小：( %5 x %6 )")
                                       .arg(x).arg(y).arg(x + w).arg(y + h).arg(w).arg(h));  // 绘制截图范围信息文本
}

// 显示事件处理函数
void ScreenWidget::showEvent(QShowEvent *)
{
    QPoint point(-1, -1);
    screen->setStart(point);
    screen->setEnd(point);

#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
    QScreen *pscreen = QApplication::primaryScreen();
    *fullScreen = pscreen->grabWindow(0, 0, 0, screen->width(), screen->height());  // 捕获全屏图像
#else
    *fullScreen = fullScreen->grabWindow(0, 0, 0, screen->width(), screen->height());  // 旧版本Qt捕获全屏图像
#endif

    // 设置透明度实现模糊背景
    QPixmap pix(screen->width(), screen->height());
    pix.fill((QColor(160, 160, 160, 200)));  // 创建一个半透明的图像
    bgScreen = new QPixmap(*fullScreen);  // 创建背景图像对象
    QPainter p(bgScreen);
    p.drawPixmap(0, 0, pix);  // 将半透明图像绘制到背景图像上
}

void ScreenWidget::keyPressEvent(QKeyEvent* event)
{
    if (event->key() == Qt::Key_Escape) { // 检测是否按下 ESC 键
        this->hide(); // 隐藏窗口，退出截图功能
    }
    else {
        QWidget::keyPressEvent(event); // 其他按键事件交给父类处理
    }
}

void ScreenWidget::hideEvent(QHideEvent* event)
{
    this->releaseKeyboard(); // 释放键盘捕获
    QWidget::hideEvent(event);
}

// 保存当前截图
void ScreenWidget::saveScreen()
{
    int x = screen->getLeftUp().x();
    int y = screen->getLeftUp().y();
    int w = screen->getRightDown().x() - x;
    int h = screen->getRightDown().y() - y;

    QString folderPath = QString("%1/Screen").arg(qApp->applicationDirPath());
    QDir().mkpath(folderPath);
    QString fileName = QString("%1/screen_%2.png").arg(folderPath).arg(STRDATETIME);  // 构建文件名
    
    fullScreen->copy(x, y, w, h).save(fileName, "png");  // 保存截图区域图像
    close();  // 关闭截图窗口
}

// 保存全屏截图
void ScreenWidget::saveFullScreen()
{
    QString folderPath = QString("%1/Screen").arg(qApp->applicationDirPath());
    QDir().mkpath(folderPath);
    QString fileName = QString("%1/full_%2.png").arg(folderPath).arg(STRDATETIME);  // 构建文件名
    fullScreen->save(fileName, "png");  // 保存全屏图像
    close();  // 关闭截图窗口
}

// 保存当前截图到指定位置
void ScreenWidget::saveScreenOther()
{
    QString name = QString("%1.png").arg(STRDATETIME);
    QString fileName = QFileDialog::getSaveFileName(this, "保存图片", name, "png Files (*.png)");  // 弹出文件对话框选择保存位置
    if (!fileName.endsWith(".png")) {
        fileName += ".png";  // 确保文件名以.png结尾
    }

    if (fileName.length() > 0) {
        int x = screen->getLeftUp().x();
        int y = screen->getLeftUp().y();
        int w = screen->getRightDown().x() - x;
        int h = screen->getRightDown().y() - y;
        fullScreen->copy(x, y, w, h).save(fileName, "png");  // 保存截图区域图像到指定位置
        close();  // 关闭截图窗口
    }
}

// 保存全屏截图到指定位置
void ScreenWidget::saveFullOther()
{
    QString name = QString("%1.png").arg(STRDATETIME);
    QString fileName = QFileDialog::getSaveFileName(this, "保存图片", name, "png Files (*.png)");  // 弹出文件对话框选择保存位置
    if (!fileName.endsWith(".png")) {
        fileName += ".png";  // 确保文件名以.png结尾
    }

    if (fileName.length() > 0) {
        fullScreen->save(fileName, "png");  // 保存全屏图像到指定位置
        close();  // 关闭截图窗口
    }
}

// 鼠标移动事件处理函数
void ScreenWidget::mouseMoveEvent(QMouseEvent *e)
{
    if (screen->getStatus() == Screen::SELECT) {
        screen->setEnd(e->pos());  // 更新结束位置
    } else if (screen->getStatus() == Screen::MOV) {
        QPoint p(e->x() - movPos.x(), e->y() - movPos.y());
        screen->move(p);  // 移动截图区域
        movPos = e->pos();  // 更新移动起始位置
    }

    this->update();  // 更新窗口内容
}

// 鼠标按下事件处理函数
void ScreenWidget::mousePressEvent(QMouseEvent *e)
{
    int status = screen->getStatus();

    if (status == Screen::SELECT) {
        screen->setStart(e->pos());  // 设置起始位置
    } else if (status == Screen::MOV) {
        if (screen->isInArea(e->pos()) == false) {
            screen->setStart(e->pos());
            screen->setStatus(Screen::SELECT);  // 如果鼠标点击位置不在截图区域内，则重新开始选择
        } else {
            movPos = e->pos();
            this->setCursor(Qt::SizeAllCursor);  // 设置鼠标光标为全向调整大小光标
        }
    }

    this->update();  // 更新窗口内容
}

// 鼠标释放事件处理函数
void ScreenWidget::mouseReleaseEvent(QMouseEvent *)
{
    if (screen->getStatus() == Screen::SELECT) {
        screen->setStatus(Screen::MOV);  // 选择完成后进入移动状态
    } else if (screen->getStatus() == Screen::MOV) {
        this->setCursor(Qt::ArrowCursor);  // 恢复默认箭头光标
    }
}

// 右键菜单事件处理函数
void ScreenWidget::contextMenuEvent(QContextMenuEvent *)
{
    this->setCursor(Qt::ArrowCursor);  // 恢复默认箭头光标
    menu->exec(cursor().pos());  // 在鼠标位置显示右键菜单
}
